#include <myheader.h>
#define PER_COPY_SIZE 1024*1024*8 //每次复制8MB
/*Usage:使用mmap函数进行大文件的复制,必须使用循环复制，因为内存空间太小，不能映射
 *整个文件  参数：src_path,dest_path */
int main(int argc, char *argv[])
{
    ARGC_CHECK(argc,3);
    //打开源文件,只读模式
    int src_fd = open(argv[1],O_RDONLY);
    ERROR_CHECK(src_fd,-1,"open_src");
    //获得源文件的大小
    off_t src_size = lseek(src_fd,0,SEEK_END);
    ERROR_CHECK(src_size,-1,"lseek_src");
    //判断是否为空文件
    if(src_size == 0) {
        printf("empty file!\n");
        int src_close_ret = close(src_fd);
        ERROR_CHECK(src_close_ret,-1,"src_close");
        return 1;
    }
    //打开目标文件
    //注意必须可读可写，只可以写的话无法进行映射
    int dest_fd = open(argv[2],O_RDWR | O_CREAT | O_TRUNC, 644);
    ERROR_CHECK(dest_fd,-1,"open_dest");
    //将目标文件扩展到何时大小
    int ftrancate_ret = ftruncate(dest_fd,src_size);
    ERROR_CHECK(ftrancate_ret,-1,"ftrancate_dest");
    //设置初始偏移量
    ssize_t cur_copy_size = 0;
    //进行循环复制
    while(cur_copy_size < src_size){
        //判断是否是最后一次copy

        ssize_t copy_size_this_time = ((src_size - cur_copy_size) < PER_COPY_SIZE) ? (src_size - cur_copy_size) : PER_COPY_SIZE;
        //映射src文件区域，一定注意分页对其，即每次mmap都是4096的倍数
        void *src_mmp = mmap(NULL,copy_size_this_time,PROT_READ,MAP_SHARED,src_fd,cur_copy_size);
        ERROR_CHECK(src_mmp,MAP_FAILED,"src_mmap");


        //映射dest文件

        void *dest_mmp = mmap(NULL,copy_size_this_time,PROT_READ|PROT_WRITE,
                              MAP_SHARED,dest_fd,cur_copy_size);
        ERROR_CHECK(dest_mmp,MAP_FAILED,"dest_mmap");

        //进行复制文件
        memcpy(dest_mmp,src_mmp,copy_size_this_time);

        //关闭映射区域
        int src_munmap_ret =  munmap(src_mmp,copy_size_this_time);
        ERROR_CHECK(src_munmap_ret,-1,"dest_munmap");
        int dest_munmap_ret =  munmap(dest_mmp,copy_size_this_time);
        ERROR_CHECK(dest_munmap_ret,-1,"dest_munmap");
        cur_copy_size += copy_size_this_time;
    }
    int src_close_ret = close(src_fd);
    ERROR_CHECK(src_close_ret,-1,"src_close");
    int dest_close_ret = close(dest_fd);
    ERROR_CHECK(dest_close_ret,-1,"dest_close");
    return 0;
}

